package com.cwz.core.captcha.core;

import com.cwz.core.captcha.common.ConstArray;
import com.cwz.core.captcha.common.Fonts;
import com.cwz.core.captcha.support.CaptchaType;
import com.cwz.core.utils.sign.Base64;

import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.security.SecureRandom;

/**
 * @program: w-demo
 * @description: 验证码生成抽象类
 * @author: Wen
 **/
public abstract class AbstractCaptcha {
	public static final SecureRandom RANDOM = new SecureRandom();
	private Font font;
	// 验证码字符长度，默认情况下缺省值为5
	protected int length = 5;
	// 验证码图片的宽度，默认的缺省值为160
	protected int width = 160;
	// 验证码图片的高度，默认缺省值为50
	protected int height = 50;
	protected CaptchaType type;
	protected String code;
	protected char[] expr;

	public AbstractCaptcha() {
		this.type = CaptchaType.DEFAULT;
		this.code = null;
		this.expr = new char[this.length];
	}

	public abstract void render(OutputStream out);

	public abstract String toBase64();

	protected char[] captcha() {
		char[] codes = new char[this.length];

		for(int i = 0; i < codes.length; ++i) {
			switch(this.type) {
				case ARITHMETIC:
					codes[i] = arithmeticExp(i, false);
					break;
				case ARITHMETIC_ZH:
					codes[i] = arithmeticExp(i, true);
					break;
				case WORD:
					codes[i] = nextChar(1);
					break;
				case WORD_LOWER:
					codes[i] = nextChar(2);
					break;
				case WORD_UPPER:
					codes[i] = nextChar(3);
					break;
				case WORD_NUMBER_LOWER:
					codes[i] = nextChar(4);
					break;
				case WORD_NUMBER_UPPER:
					codes[i] = nextChar(5);
					break;
				case NUMBER:
					codes[i] = ConstArray.NUM_ARABIC[nextInt(ConstArray.NUM_ARABIC.length)];
					break;
				case NUMBER_ZH_CN:
					codes[i] = ConstArray.NUM_ZH_CN[nextInt(ConstArray.NUM_ZH_CN.length)];
					break;
				case NUMBER_ZH_HK:
					codes[i] = ConstArray.NUM_ZH_HK[nextInt(ConstArray.NUM_ZH_HK.length)];
					break;
				case CHINESE:
					codes[i] = ConstArray.MODERN_CHINESE[nextInt(ConstArray.MODERN_CHINESE.length)];
					break;
				default:
					codes[i] = nextChar();
			}
		}

		if (!this.type.equals(CaptchaType.ARITHMETIC) && !this.type.equals(CaptchaType.ARITHMETIC_ZH)) {
			this.code = new String(codes);
		} else {
			this.code = arithmeticResult(codes);
		}

		this.expr = codes;
		return codes;
	}

	public static int nextInt(int start, int end) {
		return Math.min(start, end) + nextInt(Math.abs(start - end));
	}

	public static int nextInt(int end) {
		return RANDOM.nextInt(end);
	}

	public static char nextChar() {
		return ConstArray.NUM_EN_MIX[nextInt(ConstArray.NUM_EN_MIX.length)];
	}

	public static char nextChar(int type) {
		switch(type) {
			case 1:
				return ConstArray.EN_MIX[nextInt(ConstArray.EN_MIX.length)];
			case 2:
				return ConstArray.EN_LOWER[nextInt(ConstArray.EN_LOWER.length)];
			case 3:
				return ConstArray.EN_UPPER[nextInt(ConstArray.EN_UPPER.length)];
			case 4:
				return ConstArray.NUM_EN_MIX_LOWER[nextInt(ConstArray.NUM_EN_MIX_LOWER.length)];
			case 5:
				return ConstArray.NUM_EN_MIX_UPPER[nextInt(ConstArray.NUM_EN_MIX_UPPER.length)];
			default:
				return nextChar();
		}
	}

	protected Color color(int start, int end) {
		start = Math.min(start, 255);
		end = Math.min(end, 255);
		int r = nextInt(start, end);
		int g = nextInt(start, end);
		int b = nextInt(start, end);
		return new Color(r, g, b);
	}

	protected Color color() {
		int[] rgb = ConstArray.COLORS[nextInt(ConstArray.COLORS.length)];
		return new Color(rgb[0], rgb[1], rgb[2]);
	}

	public String toBase64(String type) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		this.render(outputStream);
		return type + Base64.encode(outputStream.toByteArray());
	}

	public String getCaptchaCode() {
		this.checkCode();
		return this.code;
	}

	public char[] text() {
		this.checkCode();
		return this.expr;
	}

	public void drawLine(Graphics2D g) {
		this.drawLine(nextInt(3, 5), g);
	}

	public void drawLine(int size, Graphics2D g) {
		size = Math.max(size, 3);

		for(int i = 0; i < size; ++i) {
			g.setColor(this.color());
			int x = nextInt(5);
			int y = nextInt(this.height);
			int _width = nextInt(this.width - 5, this.width);
			int _height = nextInt(this.height);
			g.drawLine(x, y, _width, _height);
		}

	}

	public void drawOval(Graphics2D g) {
		this.drawOval(nextInt(3, 8), g);
	}

	public void drawOval(int size, Graphics2D g) {
		size = Math.max(size, 3);

		for(int i = 0; i < size; ++i) {
			g.setColor(this.color());
			g.drawOval(nextInt(this.width - 20), nextInt(this.height - 10), nextInt(5, 15), nextInt(5, 15));
		}

	}

	public void drawBezierLine(Graphics2D g) {
		this.drawBezierLine(nextInt(2, 5), g);
	}

	public void drawBezierLine(int size, Graphics2D g) {
		size = Math.max(size, 2);

		for(int i = 0; i < size; ++i) {
			g.setColor(this.color());
			int x1 = 5;
			int y1 = nextInt(5, this.height / 2);
			int x2 = this.width - 5;
			int y2 = nextInt(this.height / 2, this.height - 5);
			int ctrlX = nextInt(this.width / 4, this.width / 4 * 3);
			int ctrlY = nextInt(5, this.height - 5);
			int ctrlX1;
			if (nextInt(2) == 0) {
				ctrlX1 = y1;
				y1 = y2;
				y2 = ctrlX1;
			}

			if (nextInt(2) == 0) {
				QuadCurve2D shape = new QuadCurve2D.Double();
				shape.setCurve((double)x1, (double)y1, (double)ctrlX, (double)ctrlY, (double)x2, (double)y2);
				g.draw(shape);
			} else {
				ctrlX1 = nextInt(this.width / 4, this.width / 4 * 3);
				int ctrlY1 = nextInt(5, this.height - 5);
				CubicCurve2D shape = new java.awt.geom.CubicCurve2D.Double((double)x1, (double)y1, (double)ctrlX, (double)ctrlY, (double)ctrlX1, (double)ctrlY1, (double)x2, (double)y2);
				g.draw(shape);
			}
		}

	}

	public Font getFont() {
		if (this.font == null) {
			this.font = Fonts.getInstance().defaultFont();
		}

		return this.font;
	}

	public void setFont(Font font) {
		this.font = font;
	}

	public int getLength() {
		return this.length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public int getWidth() {
		return this.width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return this.height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public CaptchaType getType() {
		return this.type;
	}

	public void setType(CaptchaType type) {
		this.type = type;
	}

	public String getCode() {
		return this.code;
	}

	public char[] getExpr() {
		return this.expr;
	}

	public void checkCode() {
		if (this.code == null || this.code.trim().equals("")) {
			this.captcha();
		}

	}

	private static char arithmeticExp(int i, boolean zh_cn) {
		if (zh_cn) {
			switch(i) {
				case 0:
				case 2:
					return ConstArray.NUM_ZH_CN[nextInt(ConstArray.NUM_ZH_CN.length)];
				case 1:
					return ConstArray.OPERATOR_ZH[nextInt(ConstArray.OPERATOR_ZH.length)];
				case 3:
					return '=';
				default:
					return '?';
			}
		} else {
			switch(i) {
				case 0:
				case 2:
					return ConstArray.NUM_ARABIC[nextInt(ConstArray.NUM_ARABIC.length)];
				case 1:
					return ConstArray.OPERATOR_EN[nextInt(ConstArray.OPERATOR_EN.length)];
				case 3:
					return '=';
				default:
					return '?';
			}
		}
	}

	private static String arithmeticResult(char[] codes) {
		char operator = codes[1];
		int num_1 = analysisChar(codes[0]);
		int num_2 = analysisChar(codes[2]);
		if (operator != '+' && operator != 21152) {
			if (operator != '-' && operator != 20943) {
				return operator != '*' && operator != 'x' && operator != 'X' && operator != 20056 ? "0" : String.valueOf(num_1 * num_2);
			} else {
				return String.valueOf(num_1 - num_2);
			}
		} else {
			return String.valueOf(num_1 + num_2);
		}
	}

	private static int analysisChar(char character) {
		if (character != '0' && character != '零') {
			if (character != '1' && character != 22777 && character != 19968) {
				if (character != '2' && character != '贰' && character != 20108) {
					if (character != '3' && character != 21441 && character != 19977) {
						if (character != '4' && character != '肆' && character != 22235) {
							if (character != '5' && character != 20237 && character != 20116) {
								if (character != '6' && character != '陆' && character != 20845) {
									if (character != '7' && character != 26578 && character != 19971) {
										if (character != '8' && character != 25420 && character != 20843) {
											return character != '9' && character != 29590 && character != 20061 ? 0 : 9;
										} else {
											return 8;
										}
									} else {
										return 7;
									}
								} else {
									return 6;
								}
							} else {
								return 5;
							}
						} else {
							return 4;
						}
					} else {
						return 3;
					}
				} else {
					return 2;
				}
			} else {
				return 1;
			}
		} else {
			return 0;
		}
	}
}
